package com.livestocksolutions;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 11 July 2012 - Initial version.
 *
 *  1.1 : 16 July 2012 - Class now implements Serializable
 *  
 *  1.2 : 24 September 2012 - Added Update methods that will allow the comparing and updation of a similar Object.
 *                   Serialisation method no longer requires Chemicine implementing Serializable.
 *  
 *  2.0 : 27 December 2012 - Remake of class to have objects more as templates than actual real references.
 *  
 *  2.1 : 06 March 2013 - Use of generics to reduce duplicate code, T passed to UsedItem<T>.
 */

/**
 * A class for all purchased Chemicals and Medicines.<br>
 * This class is not to be directly instantiated, but instead use the subclasses of this class
 * @author Adam Parr
 * @version 2.1
 * 
 * @.modified 07 March 2013
 * 
 * @param <T> A sub class of UsedItem that the UsableItem accepts as a Mixture.
 */
public abstract class Chemicine<T extends UsedItem> extends UsableItem<T>{
	
	/**
	 * Constant for Withhold Period to indicate that it is measured in Minutes.
	 */
	public static final int MINUTE 	= 0;
	/**
	 * Constant for Withhold Period to indicate that it is measured in Hours.
	 */
	public static final int HOUR 	= 1;
	/**
	 * Constant for Withhold Period to indicate that it is measured in Days.
	 */
	public static final int DAY 	= 2;
	/**
	 * Constant for Withhold Period to indicate that it is measured in Months.
	 */
	public static final int MONTH 	= 3;
	/**
	 * Constant for Withhold Period to indicate that it is measured in Years.
	 */
	public static final int YEAR 	= 4;
	/**
	 * The period at which this Chemical/medicine's effects on the applied Paddock, Bovine, or other
	 * are considered to be no-longer active.
	 */
	protected double withholdPeriod;
	
	/**
	 * The units the {@link #withholdPeriod} should be displayed as.
	 * @see #MINUTE
	 * @see #HOUR
	 * @see #DAY
	 * @see #MONTH
	 * @see #YEAR
	 */
	protected int withholdPeriodUnits=DAY;
	
	/**
	 * A Description of the Application methods and steps to follow
	 */
	protected String applicationDesc;
	
	/**
	 * Get the withhold period of this Chemical/medicine.
	 * @return the withholdPeriod of this Chemical/medicine.
	 */
	public double getWithholdPeriod() {
		return withholdPeriod;
	}
	
	/**
	 * Get the withhold period units of this Chemical/medicine.
	 * @return the withholdPeriodUnits.
	 * 
	 * @see #MINUTE
	 * @see #HOUR
	 * @see #DAY
	 * @see #MONTH
	 * @see #YEAR
	 */
	public int getWithholdPeriodUnit() {
		return withholdPeriodUnits;
	}
	
	/**
	 * Return the units of measurement of the withhold period as a human readable format.
	 * @param value The integer version of the withhold period units.
	 * @return String displaying "Minute", "Hour", "Day", "Month", "Year"
	 * 
	 * @see #MINUTE
	 * @see #HOUR
	 * @see #DAY
	 * @see #MONTH
	 * @see #YEAR
	 */
	public static String getWithholdPeriodUnitString(int value){
		switch(value){
		case MINUTE:
			return "Minute";
		case HOUR:
			return "Hour";
		case MONTH:
			return "Month";
		case YEAR:
			return "Year";
		case DAY:
		default:
			return "Day";
		}
	}
	
	/**
	 * Return the integer representation of the given withhold period unit measurement String.
	 * @param value withhold period unit in a human readable format.
	 * @return {@link #DAY} if value can not be determined, otherwise one of {@link #MINUTE}, {@link #HOUR}, {@link #DAY}
	 * , {@link #MONTH} or {@link #YEAR}
	 * 
	 * @see #MINUTE
	 * @see #HOUR
	 * @see #DAY
	 * @see #MONTH
	 * @see #YEAR
	 */
	public static int getWitholdUnitInt(String value){
		if(value.startsWith("D")||value.startsWith("d")){
			return DAY;
		}else if(value.startsWith("H")||value.startsWith("h")){
			return HOUR;
		}else if(value.startsWith("Y")||value.startsWith("Y")){
			return YEAR;
		}else if(value.startsWith("MI")||value.startsWith("Mi")||value.startsWith("mi")||value.startsWith("mI")){
			return MINUTE;
		}else if(value.startsWith("MO")||value.startsWith("Mo")||value.startsWith("mo")||value.startsWith("mO")){
			return MONTH;
		}
		// Value could not be determined, assume it is day.
		return DAY;
	}
	
	/**
	 * set the withhold period of this Chemical/medicine
	 * @param withholdPeriod the withhold period to set.<br>
	 * In the format of "30 DAY", or "2 MONTH", or "1 YEAR"<br>
	 * 
	 * @deprecated Use {@link #setWithholdPeriod(double,int)} instead.
	 */
	@Deprecated
	public void setWithholdPeriod(String withholdPeriod) {
		String[] s = withholdPeriod.split(" ");
		if(s.length > 1){
			setWithholdPeriod(Double.parseDouble(s[0]),getWitholdUnitInt(s[1]));
		}else{
			setWithholdPeriod(Integer.parseInt(withholdPeriod),DAY);
		}
	}
	
	/**
	 * set the withhold period of this Chemical/medicine.
	 * @param withholdPeriod The withhold period to set.
	 * @param unitMeasurement The unit of measurement that withholdPeriod is in.
	 * 
	 * @see #MINUTE
	 * @see #HOUR
	 * @see #DAY
	 * @see #MONTH
	 * @see #YEAR
	 */
	public void setWithholdPeriod(double withholdPeriod, int unitMeasurement){
		this.withholdPeriod = withholdPeriod;
		withholdPeriodUnits=unitMeasurement;
		update.put("WITHHOLD", new UpdateHistory(String.valueOf(this.withholdPeriod)));
		update.put("WITHHOLDUNITS", new UpdateHistory(String.valueOf(this.withholdPeriodUnits)));
	}
	
	/**
	 * Get the Application description/method for this Chemical/medicine
	 * @return Application method of this particular Chemical/medicine
	 */
	public String getApplicationDesc(){
		return applicationDesc;
	}
	
	/**
	 * Set the description for describing how to apply this Chemical/medicine to the subject
	 * @param applicationDesc A description describing the application method for this Chemical/medicine was apart of
	 */
	public void setApplicationDesc(String applicationDesc){
		if(this.applicationDesc!=applicationDesc){
			this.applicationDesc=applicationDesc;
			update.put("APPLICATIONDESC", new UpdateHistory(this.applicationDesc));
		}
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes Chemicine that represents this Object in a different version.
	 * @return <tt>true</tt> if changes is not null and changes does not equal this object, <tt>false</tt> otherwise.
	 */
	public boolean update(Chemicine<T> changes){
		if(changes!=null && this!=changes){
			super.update(changes);
			
			if(updateBefore("WITHHOLD",changes)){
				withholdPeriod=Double.parseDouble(changes.update.get("WITHHOLD").getValue());
				update.put("WITHHOLD", new UpdateHistory(String.valueOf(withholdPeriod)));
			}
			if(updateBefore("APPLICATIONDESC",changes)){
				applicationDesc=changes.update.get("APPLICATIONDESC").getValue();
				update.put("APPLICATIONDESC", new UpdateHistory(applicationDesc));
			}
			
			if(updateBefore("WITHHOLDUNITS",changes)){
				withholdPeriodUnits=Integer.parseInt(changes.update.get("WITHHOLDUNITS").getValue());
				update.put("WITHHOLDUNITS", new UpdateHistory(String.valueOf(withholdPeriodUnits)));
			}
			return true;
		}
		return false;
	}
}
